-- Re-written by Alundaio (original: ???) -- Added more animations and fixed bug of post_combat_idle not always working -- example of this is when they stuck in threat pose after combat or continue to shoot corpse after combat class "evaluator_combat_enemy" (property_evaluator) function evaluator_combat_enemy:__init(storage, name) super(nil, name) self.st = storage self.st.timer = 0 end function evaluator_combat_enemy:evaluate() --utils_data.debug_write("post_combat_idle: eva_combat_enemy") local npc = self.object if not (npc:alive()) then return false end if (IsWounded(npc)) then return false end local best_enemy = npc:best_enemy() if (best_enemy and self.st.timer) then self.st.timer = nil end local tg = time_global() if (best_enemy == nil and self.st.timer == nil) then if (xr_conditions.surge_started() ~= true and db.storage[npc:id()].active_scheme ~= "camper") then local overrides = db.storage[npc:id()].overrides local min = (overrides and overrides.min_post_combat_time*1000) or 20000 local max = (overrides and overrides.max_post_combat_time*1000) or 25000 self.st.timer = tg + math.random(min, max) else self.st.timer = 0 end end if not (self.st.timer) then return best_enemy ~= nil end if (xr_danger.has_danger(npc)) then return false end if (tg < self.st.timer) then -- Fix freeze after combat or continual corpse shooting after combat -- Why? Because action_sudden_attack seems to be default action when no other combat action is active -- Sometimes post combat idle action doesn't execute after some actions. I don't know why. I tried removing all the preconditions (except property_enemy) to no avail. local mgr = npc:motivation_action_manager() if (mgr and mgr:initialized()) then local combat_action = mgr:action(stalker_ids.action_combat_planner) local combat_action_planner = cast_planner(combat_action) if (combat_action_planner and combat_action_planner:initialized()) then local cid = combat_action_planner:current_action_id() if (cid == nil or cid == stalker_ids.action_sudden_attack) then self.st.timer = 0 return false end end end return true end if not (self.st.animation) then return false end self.st.animation:set_state(nil) return self.st.animation.states.anim_marker ~= nil end ---------------------------------------------------------------------------------------------------------------------- class "action_post_combat_wait" (action_base) function action_post_combat_wait:__init(npc, storage, action_name) super(nil, action_name) self.st = storage end function action_post_combat_wait:initialize() action_base.initialize(self) --utils_data.debug_write("action_post_combat_wait:init") local bw = self.object:best_weapon() if (bw and IsWeapon(bw)) then self.object:set_item(object.idle, bw) end self.object:set_mental_state(anim.danger) self.object:set_body_state(move.crouch) self.object:set_movement_type(move.stand) self.object:set_sight(look.danger, nil, 0) self.object:set_desired_position() self.object:set_desired_direction() self.anim_st = { animstate = { states = {anim_marker = nil } } } self.st.animation = state_mgr_animation.animation(self.object, self.anim_st, "state_mgr_animation_list") self.anim_started = false self.st.stage = 0 end function action_post_combat_wait:execute() action_base.execute(self) --utils_data.debug_write("action_post_combat_wait:execute start") if (self.anim_started) then --utils_data.debug_write("action_post_combat_wait:execute end 4") return end local npc = self.object if (npc:in_smart_cover()) then --utils_data.debug_write("action_post_combat_wait:execute end 5") return end local bw = self.object:best_weapon() if (bw and IsWeapon(bw)) then self.object:set_item(object.idle, bw) end if (npc:path_type() ~= game_object.level_path) then npc:set_path_type(game_object.level_path) end if (self.st.stage == 0) then local new_vid = utils_obj.find_random_cover(npc,npc:position(),10,math.random(15,25)) if (new_vid) then npc:set_body_state(move.standing) npc:set_detail_path_type( move.line ) npc:set_mental_state(anim.free) npc:set_movement_type(move.walk) npc:set_sight(look.path_dir,nil,0) self.st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,new_vid) self.st.stage = 1 --utils_data.debug_write("action_post_combat_wait:execute end 1") return end elseif (self.st.stage == 1) then if (self.st.lvid and npc:level_vertex_id() ~= self.st.lvid) then self.st.lvid = utils_obj.send_to_nearest_accessible_vertex(npc,self.st.lvid) --utils_data.debug_write("action_post_combat_wait:execute end 2") return end end if (state_mgr_weapon.weapon_locked(npc)) then return end self.anim_started = true local comm = character_community(self.object) local state = "hide" local anims = {"idle","hide","idle_chasovoy","caution"} -- story npcs should only use base anims or it might seem strange local squad = not get_object_story_id(self.object:id()) and get_object_squad(self.object) if (squad) then if (squad:commander_id() == self.object:id()) then table.insert(anims,"give_orders") table.insert(anims,"raciya") table.insert(anims,"cr_raciya") xr_sound.set_sound_play(self.object:id(), "post_combat_wait") else local commander = db.storage[squad:commander_id()] and db.storage[squad:commander_id()].object if (commander and self.object:position():distance_to_sqr(commander:position()) >= 625) then table.insert(anims,"raciya") table.insert(anims,"cr_raciya") end end end state = anims[math.random(#anims)] self.st.animation:set_state(state) self.st.animation:set_control() local dir = npc:direction() npc:set_sight(look.direction,vector():set(-dir.x,-dir.y,-dir.z)) --utils_data.debug_write("action_post_combat_wait:execute end 3") end function action_post_combat_wait:finalize() local squad = get_object_squad(self.object) if (squad and squad:commander_id() == self.object:id()) then xr_sound.set_sound_play(self.object:id(), "post_combat_relax") end if self.anim_started == true and self.st.animation then self.st.animation:set_state(nil, true) end self.st.stage = nil self.st.animation = nil action_base.finalize(self) end function add_post_combat_idle(npc) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end --printf("ACTION_ALIFE_ID(post_combat_idle.add_post_combat_idle): " .. tostring(stalker_ids.action_combat_planner)) local combat_action = manager:action(stalker_ids.action_combat_planner) --combat_action:show("") local combat_action_planner = cast_planner(combat_action) if not combat_action or not combat_action_planner then return end db.storage[npc:id()].post_combat_wait = {} local storage = db.storage[npc:id()].post_combat_wait manager:remove_evaluator(stalker_ids.property_enemy) manager:add_evaluator(stalker_ids.property_enemy, evaluator_combat_enemy(storage, "evaluator_combat_enemy")) combat_action_planner:remove_evaluator(stalker_ids.property_enemy) combat_action_planner:add_evaluator(stalker_ids.property_enemy, evaluator_combat_enemy(storage, "evaluator_combat_enemy")) combat_action_planner:remove_action(stalker_ids.action_post_combat_wait) local new_action = this.action_post_combat_wait(npc, storage, "action_post_combat_wait") if not new_action then return end new_action:add_precondition(world_property(stalker_ids.property_enemy, true)) new_action:add_precondition(world_property(stalker_ids.property_pure_enemy, false)) new_action:add_precondition(world_property(stalker_ids.property_critically_wounded, false)) --new_action:add_precondition(world_property(stalker_ids.property_danger_grenade, false)) new_action:add_effect(world_property(stalker_ids.property_enemy, false)) combat_action_planner:add_action(stalker_ids.action_post_combat_wait, new_action) end